/*
 * app_bl0942.c
 *
 *  Created on: 2023年11月22日
 *      Author: zlw
 */
#include "app_bl0942.h"
#ifdef SM_WS

#include "stddef.h"
#include "softwareMultiTimer.h"
#include "app_log.h"
#include "device.h"
#include "stdlib.h"
#include "app_api.h"
#include "app_key_value.h"

Buffer_t bl0942_com_TXBuff;
Buffer_t bl0942_com_RXBuff;

uartData_t bl0942Pkt = {0x00};

static uint16_t voltageGet_t(void);

static void bl0942_comInit (void)
{
  CMU_ClockEnable(cmuClock_HFXO, true);
  CMU_ClockEnable(cmuClock_GPIO, true);

  CMU_ClockEnable(cmuClock_EUSART1, true);

  GPIO_PinModeSet(BL0942_TX_PORT, BL0942_TX_PIN, gpioModePushPull, 1);
  GPIO_PinModeSet(BL0942_RX_PORT, BL0942_RX_PIN, gpioModeInputPull, 1);

  EUSART_UartInit_TypeDef init = BL0942_COM_DEFAULT;
  EUSART_UartInitHf(BL0942_COM, &init);

  GPIO->EUSARTROUTE[1].TXROUTE = (BL0942_TX_PORT << _GPIO_EUSART_TXROUTE_PORT_SHIFT)
      | (BL0942_TX_PIN << _GPIO_EUSART_TXROUTE_PIN_SHIFT);
  GPIO->EUSARTROUTE[1].RXROUTE = (BL0942_RX_PORT << _GPIO_EUSART_RXROUTE_PORT_SHIFT)
      | (BL0942_RX_PIN << _GPIO_EUSART_RXROUTE_PIN_SHIFT);
  GPIO->EUSARTROUTE[1].ROUTEEN = GPIO_EUSART_ROUTEEN_RXPEN | GPIO_EUSART_ROUTEEN_TXPEN;

  // Enable RX interrupts
  NVIC_ClearPendingIRQ(EUSART1_RX_IRQn);
  NVIC_EnableIRQ(EUSART1_RX_IRQn);
  EUSART_IntEnable(EUSART1, EUSART_IEN_RXFL);
  // Enable TX interrupts
  NVIC_ClearPendingIRQ(EUSART1_TX_IRQn);
  NVIC_EnableIRQ(EUSART1_TX_IRQn);
  EUSART_IntEnable(EUSART1, EUSART_IEN_TXC);

  //EUSART_Enable(COM_USART, eusartEnable);
  EUSART_IntEnable(EUSART1, EUSART_IEN_RXFL);
  EUSART_IntEnable(EUSART1, EUSART_IEN_TXC);

  // Finally enable EUSART
  bufferInit(&bl0942_com_TXBuff,BL0942_COM_BUFF_SIZE );//初始化环形队列
  bufferInit(&bl0942_com_RXBuff,BL0942_COM_BUFF_SIZE );//初始化环形队列
}

static void bl0942_com_RX_IRQHandler(void)
{
  if( EUSART_IntGet( BL0942_COM ) & EUSART_IF_RXFL ){
    bufferAdd( &bl0942_com_RXBuff, EUSART_Rx(BL0942_COM));
    EUSART_IntClear(BL0942_COM, EUSART_IF_RXFL);//读完数据再清标志
  }
}

static void bl0942_com_TX_IRQHandler(void)
{
  if(EUSART_IntGet(BL0942_COM) & EUSART_IF_TXC ){
      EUSART_IntClear(BL0942_COM, EUSART_IF_TXC);
  }

  if(bl0942_com_TXBuff.count !=0 ){
      EUSART_Tx(BL0942_COM, *bufferRemove(&bl0942_com_TXBuff) );
  }
}

void EUSART1_RX_IRQHandler(void)
{
  bl0942_com_RX_IRQHandler();
}

void EUSART1_TX_IRQHandler(void)
{
  bl0942_com_TX_IRQHandler();
}

void bl0942_com_Txbuff(const char *ptr, int len)
{
  int i = 0;
  for(i = 0; i < len; i++){
      bufferAdd(&bl0942_com_TXBuff, *ptr++);   //环形队列ADD
  }
    if( (EUSART_StatusGet(BL0942_COM) & EUSART_IF_TXIDLE)  && (bl0942_com_TXBuff.count != 0) ){  //EUSART IDLE
        EUSART_IntSet(BL0942_COM, EUSART_IF_TXC);
    }
}

static uint8_t rf_frame_check(uartData_t *pkt) {
  uint16_t i = 0;
  uint16_t sum = 0x58;
  uint8_t check = 0x00;

  sum += pkt->head;
  for (i = 0; i < 21; i++) {
    sum += pkt->data[i];
  }

  check = (sum&0xff);
  check = (~check);
  return check;
}

static uint8_t frame_bufer_check(uint8_t *data, uint8_t len) {
  uint16_t i = 0;
  uint16_t sum = 0x00;
  uint8_t check = 0x00;

  for (i = 0; i < len; i++) {
    sum += data[i];
  }

  check = (sum&0xff);
  check = (~check);
  return check;
}

static int rfx_frame_parse(uint8_t data, uartData_t *oPkt)
{
  uartData_t *pkt = oPkt;
  switch (pkt->recv_state) {
    case PKT_STAT_HEAD:
      if (data == 0x55) {
          pkt->head = data;
          pkt->recv_len = 0;
          pkt->recv_state = PKT_DATA;
      }
      break;

    case PKT_DATA:
      pkt->data[pkt->recv_len++] = data;
      if (pkt->recv_len == ELECTRIC_DATA_LEN) {
          pkt->recv_state = PKT_CHECK_SUM;
      }
      break;

    case PKT_CHECK_SUM:
      pkt->checkSum = data;
      pkt->recv_state = PKT_STAT_HEAD;
      if (rf_frame_check(pkt) == pkt->checkSum) {
          return 0;
      }
      break;
  }
  return -3;
}

static uint32_t currentCnt = 0;
static uint32_t voltageCnt = 0;
static uint32_t powerCnt = 0;
static uint32_t energyCnt = 0;

#define DATA_MAX_CNT    10
static uint8_t currCalibration_t[DATA_MAX_CNT] = {0x00};
static uint8_t volCalibration_t[DATA_MAX_CNT] = {0x00};
static uint8_t powCalibration_t[DATA_MAX_CNT] = {0x00};
static uint8_t currIndex = 0;
static uint8_t volIndex = 0;
static uint8_t powIndex = 0;

static calibration_t calibration = {0};
static measure_t measureState = MEASURE_NONE;

static uint8_t data_buff_avg(uint8_t *data, uint8_t len)
{
  uint32_t sum = 0;
  uint8_t i;

  for(i=0;i<len;i++) {
      sum += data[i];
  }

  return (sum / len);
}

static uint8_t currentCalibrationGet(void)
{
  uint8_t currCalibration = 0;
  uint32_t currCalibrate_t = 0;

  if(currentCnt > 0)
    currCalibrate_t = CALIBRATION_CURRENT / currentCnt;

  if(currCalibrate_t > 30 && currCalibrate_t < 50)
    currCalibration = currCalibrate_t;

  return currCalibration;
  //return 40;
}

static uint8_t voltageCalibrationGet(void)
{
  uint8_t volCalibration = 0;
  uint32_t volCalibrate_t = 0;

  if(voltageCnt > 0)
    volCalibrate_t = CALIBRATION_VOLTAGE / voltageCnt;

  if(volCalibrate_t > 65 && volCalibrate_t < 85)
    volCalibration = volCalibrate_t;

  return volCalibration;
  //return 75;
}

static uint8_t powerCalibrationGet(void)
{
  uint8_t powCalibration = 0;
  uint32_t powCalibrate_t = 0;

  if(powerCnt > 0)
    powCalibrate_t = CALIBRATION_POWER / powerCnt;

  if(powCalibrate_t > 10 && powCalibrate_t < 30)
    powCalibration = powCalibrate_t;

  return powCalibration;
  //return 19;
}

static MultiTimer bl0937Timer[3] = {
    {.Handle = 0},
    {.Handle = 1},
    {.Handle = 2}
};

bool energyClearState = false;

static void bl0942_write_reg(uint8_t addr, uint8_t data)
{
  uint8_t txData[8] = {0x00};

  txData[0] = 0xA8;
  txData[1] = addr;
  txData[2] = data;
  txData[3] = 0x00;
  txData[4] = 0x00;
  txData[5] = frame_bufer_check(txData, 5);
  bl0942_com_Txbuff((char *)txData, 6);
}

static void bl0942_read_reg(uint8_t addr)
{
  uint8_t txData[2] = {0x00};

  txData[0] = FRAME_HEAD;
  txData[1] = addr;
  bl0942_com_Txbuff((char *)txData, 2);
}

static bool bl0942Error = false;

static void bl0937TimeroutHandlerCallback(MultiTimer* timer, void *userData)
{
  (void)userData;
  uint32_t summation = 0;
  uint32_t sumCnt = 0;

  if(timer->Handle == 0) {
      bl0942_read_reg(READ_CMD);
      softwareMultiTimerStart(EN_LP_HARD_TIMER_EM3_SYS_RTC_1, timer, MEASURE_INTERVAL,  bl0937TimeroutHandlerCallback,NULL,1);
  }
  else if(timer->Handle == 1) {
      if(getThisParam(smartPlugEnergy, &summation) != 0) {
          summation = 0;
      }
      sumCnt = 16384 * 256 * calibration.powerCalibration / 3600000;
      summation +=  (uint32_t)(sumCnt * energyCnt / 100);
      if(summation > 0) {
          energyCnt = 0;
          setThisParam(smartPlugEnergy, &summation);
          bl0942_write_reg(0x1D, 0x55);
          bl0942_write_reg(0x19, CLEAR_ENERGY_CNT);
          energyClearState = true;
      }

      softwareMultiTimerStart(EN_LP_HARD_TIMER_EM3_SYS_RTC_1, &bl0937Timer[1], ENERGY_SAVE_INTERVAL,  bl0937TimeroutHandlerCallback,NULL,1);
  }
  else if(timer->Handle == 2) {
      bl0942Error = false;
  }
}

extern uint8_t zeroState;
static uint8_t zeroStateLast = 0x00;
static voltageFreq_t frequency = FREQUENCY_50HZ;
static overCnt_t overCnt = {0x00};
static energyStateCallback stateCallback[energyStateNum] = {NULL};

static void bl0942_data_handler(uartData_t *oPkt)
{
  electricData_t electricData = {0x00};
  uint32_t voltageTemp = 0;

  if(zeroState == zeroStateLast || bl0942Error == true) {
      return ;
  }
  zeroStateLast = zeroState;

  memcpy(&electricData, oPkt->data, ELECTRIC_DATA_LEN);

  voltageTemp = electricData.vRms[0]|(electricData.vRms[1]<<8)|(electricData.vRms[2]<<16);
  if(voltageTemp == 0 && bl0937Timer[2].status == EN_MULTITIMER_STATUS_IDLE) {
      bl0942Error = true;
      softwareMultiTimerStart(EN_LP_HARD_TIMER_EM3_SYS_RTC_1, &bl0937Timer[2], 32768,  bl0937TimeroutHandlerCallback,NULL,1);
      DS_app_log_error("bl0942 err \r\n");
      return ;
  }
  voltageCnt = voltageTemp;

  currentCnt = electricData.iRms[0]|(electricData.iRms[1]<<8)|(electricData.iRms[2]<<16);
  uint32_t voltageFreq = electricData.freq[0]|(electricData.freq[1]<<8);

  if(voltageFreq >= 16400 && voltageFreq <= 16950) { //59Hz<=frequency<= 61Hz
      frequency = FREQUENCY_60HZ;
  }
  else {
      frequency= FREQUENCY_50HZ;
  }

  if((((electricData.pRms[2]) >> 7)&0x01) == 0x01) {
      powerCnt = 0;
      DS_app_log_error("pRms = 0x%02x 0x%02x 0x%02x\r\n", electricData.pRms[0], electricData.pRms[1], electricData.pRms[2]);
  }
  else {
      powerCnt = electricData.pRms[0]|(electricData.pRms[1]<<8)|(electricData.pRms[2]<<16);
  }

  if(measureState == MEASURE_CALBRATION) {
      if(currIndex < DATA_MAX_CNT && currentCalibrationGet() > 0) {
          currCalibration_t[currIndex++] = currentCalibrationGet();
      }
      if(volIndex < DATA_MAX_CNT && voltageCalibrationGet() > 0) {
          volCalibration_t[volIndex++] = voltageCalibrationGet();
      }
      if(powIndex < DATA_MAX_CNT && powerCalibrationGet() > 0) {
          powCalibration_t[powIndex++] = powerCalibrationGet();
      }
      if(currIndex == DATA_MAX_CNT) {
          calibration.currentCalibration = data_buff_avg(currCalibration_t, DATA_MAX_CNT);
      }
      if(volIndex == DATA_MAX_CNT) {
          calibration.voltageCalibration = data_buff_avg(volCalibration_t, DATA_MAX_CNT);
      }
      if(powIndex == DATA_MAX_CNT) {
          calibration.powerCalibration = data_buff_avg(powCalibration_t, DATA_MAX_CNT);
      }
      if(calibration.currentCalibration && calibration.voltageCalibration && calibration.powerCalibration) {
          setThisParam(smartPlugCalibration, &calibration);
          measureState = MEASURE_NORMAL;
          calibration_finist_callback();
      }
  }
  else {
      if(currentGet() > CURRENT_OVERLOAD) {
          overCnt.iOverCnt ++;
          if(overCnt.iOverCnt >= ENERGY_OVERLOAD_CNT) {
              overCnt.iOverCnt = 0;
              stateCallback[currentOverload](currentGet());
          }
      }
      else {
          overCnt.iOverCnt = 0;
          stateCallback[currentNormal](currentGet());
      }
      if(voltageGet_t() > 253 || (voltageGet_t() >= 132 && voltageGet_t() <= 160)) {
          overCnt.vUnderCnt = 0;
          overCnt.vnormalCnt = 0;
          overCnt.vOverCnt++;
          if(overCnt.vOverCnt >= ENERGY_OVERLOAD_CNT) {
              overCnt.vOverCnt = 0;
              stateCallback[voltageOverload](voltageGet_t());
          }
      }
      else if(voltageGet_t() < 92 || (voltageGet_t() > 160 && voltageGet_t() <= 184)) {
          overCnt.vOverCnt = 0;
          overCnt.vnormalCnt = 0;
          overCnt.vUnderCnt ++;
          if(overCnt.vUnderCnt >= ENERGY_OVERLOAD_CNT) {
              overCnt.vUnderCnt = 0;
              stateCallback[voltageUnderload](voltageGet_t());
          }
      }
      else {
          if((voltageGet_t() >= 102 && voltageGet_t() <= 127) ||
              (voltageGet_t() >= 195 && voltageGet_t() <= 240)) {
              overCnt.vnormalCnt ++;
              if(overCnt.vnormalCnt >= ENERGY_NORMAL_CNT) {
                  overCnt.vnormalCnt = 0;
                  overCnt.vOverCnt = 0;
                  overCnt.vUnderCnt = 0;
                  stateCallback[voltageNomal](voltageGet_t());
              }
          }
          if(voltageGet_t() <= 253 && voltageGet_t() > 184) {
              if(powerGet() >= 32000) {
                  overCnt.p220OverCnt++;
                  if(overCnt.p220OverCnt >= ENERGY_OVERLOAD_CNT) {
                      overCnt.p220OverCnt = 0;
                      stateCallback[powerOverload](powerGet());
                  }
              }
              else {
                  overCnt.p220OverCnt = 0;
                  stateCallback[powerNormal](powerGet());
              }
          }
          else if(voltageGet_t() <= 160 && voltageGet_t() >= 92) {
              if(powerGet() >= 16000) {
                  overCnt.p110OverCnt++;
                  if(overCnt.p110OverCnt >= ENERGY_OVERLOAD_CNT) {
                      overCnt.p110OverCnt = 0;
                      stateCallback[powerOverload](powerGet());
                  }
              }
              else {
                  overCnt.p110OverCnt = 0;
                  stateCallback[powerNormal](powerGet());
              }
          }
      }
  }
  if(energyClearState == true) {
      bl0942_write_reg(0x19, RESET_MODE_REG);
      energyClearState = false;
      bl0942_write_reg(0x1D, 0xAA);
  }
  else {
      energyCnt = electricData.cfCnt[0]|(electricData.cfCnt[1]<<8)|(electricData.cfCnt[2]<<16);
  }
#if 0
  DS_app_log_error("i rms = %ld \r\n", currentCnt);
  DS_app_log_error("v rms = %ld \r\n", voltageCnt);
  DS_app_log_error("f rms = %d \r\n", electricData.iFastRms[0]|(electricData.iFastRms[1]<<8)|(electricData.iFastRms[2]<<16));
  DS_app_log_error("p rms = %ld \r\n", powerCnt);
  DS_app_log_error("cnt = %ld \r\n", energyCnt);
  DS_app_log_error("freq = %ld \r\n", voltageFreq);
  DS_app_log_error("state = 0x%02x 0x%02x 0x%02x\r\n", electricData.state[0], electricData.state[1], electricData.state[2]);
#endif
}

void bl0942_usart_listen(void)
{
  int iret = 0;
  uint8_t *data=NULL;
  if(bufferIsEmpty(&bl0942_com_RXBuff) != true){
      data = bufferRemove(&bl0942_com_RXBuff);
      iret = rfx_frame_parse(data[0], &bl0942Pkt);
      if(iret == 0){
          bl0942_data_handler(&bl0942Pkt);
      }
  }
}

static void bl0937_measure_start(void)
{
  if(0 != getThisParam(smartPlugCalibration, &calibration)
      || calibration.currentCalibration == 0
      || calibration.voltageCalibration == 0
      || calibration.powerCalibration == 0) {
      measureState = MEASURE_CALBRATION;
  }
  else if(0 == getThisParam(smartPlugCalibration, &calibration)) {
      measureState = MEASURE_NORMAL;
      DS_app_log_error("currentCalibration = %d \r\n", calibration.currentCalibration);
      DS_app_log_error("voltageCalibration = %d \r\n", calibration.voltageCalibration);
      DS_app_log_error("powerCalibration = %d \r\n", calibration.powerCalibration);
  }
}

void resetCalibration(void)
{
  calibration.currentCalibration = 0;
  calibration.powerCalibration = 0;
  calibration.voltageCalibration = 0;
  measureState = MEASURE_NONE;
  setThisParam(smartPlugCalibration, &calibration);
}

void energyStateCallbackRegister(energyState_t state, energyStateCallback callback)
{
  stateCallback[state] = callback;
}

/**
 * @brief: timer capture init
 * select gpio init
 * */
void bl0942_init(void)
{
  bl0942_comInit();
  bl0937_measure_start();
  softwareMultiTimerStart(EN_LP_HARD_TIMER_EM3_SYS_RTC_1, &bl0937Timer[0], MEASURE_INTERVAL,  bl0937TimeroutHandlerCallback,NULL,1);
  softwareMultiTimerStart(EN_LP_HARD_TIMER_EM3_SYS_RTC_1, &bl0937Timer[1], ENERGY_SAVE_INTERVAL,  bl0937TimeroutHandlerCallback,NULL,1);
}

voltageFreq_t getVoltageFrequency(void)
{
  return frequency;
}

/**
 * @brief: get mersure state
 * return: measure state
 * */
measure_t measure_state_get(void)
{
  return measureState;
}

/**
 * @brief: get current
 * return :current unit:mA
 * */
uint16_t currentGet(void)
{
  uint16_t current = 0;

  current = currentCnt * calibration.currentCalibration / CURRENT_MULTI;
  return current;
}

/**
 * @brief: get voltage
 * return :voltage - 90  unit:V
 * */
static uint16_t voltageGet_t(void)
{
  uint16_t voltage = voltage = voltageCnt * calibration.voltageCalibration / VOLTAGE_MULTI;
  return voltage;
}

uint8_t voltageGet(void)
{
  uint16_t voltage_t = 0;

  if(voltageGet_t() < 90) {
      voltage_t = 90;
  }
  else if(voltageGet_t() > 255)
    voltage_t = 255;
  else {
      voltage_t = voltageGet_t();
  }

  return (voltage_t - 90);
}

/**
 * @brief: get power
 * return :power unit:0.1w
 * */
uint16_t powerGet(void)
{
  uint16_t power = 0;

  power = powerCnt * calibration.powerCalibration * 10 / POWER_MULTI;
  DS_app_log_info("power = %d \r\n", power);
  return power;
}

/**
 * @brief: get summation energy
 * return :kwh  unit:0.1 kwh
 * */
uint32_t summaEnergyGet(void)   //
{
  uint32_t summation = 0;
  uint32_t sumCnt = 0;

  if(0 != getThisParam(smartPlugEnergy, &summation)) {
      summation = 0;
  }
  sumCnt = 16384 * 256 * calibration.powerCalibration / 3600000;
  summation +=  (uint32_t)(sumCnt * energyCnt / 100);

  return (summation / 100);
}

/**
 * @brief: calibration finish callback
 * */
__WEAK void calibration_finist_callback(void)
{

}

#endif
